﻿#region

using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using DeepEarth.Map.Core;
using DeepEarth.Toolkit.Common;
using System;
using DeepEarth.Map.Core.Tiling;

#endregion

namespace DeepEarth.Toolkit.Controls
{
    public class MiniMap : MapInteractionControl
    {
        #region Properties

        const string PART_map = "PART_map";

        public static readonly DependencyProperty ZoomModifierProperty =
            DependencyProperty.Register("ZoomModifier", typeof(double), typeof(MiniMap), new PropertyMetadata(1.5));

        public static readonly DependencyProperty ReferenceViewProperty =
            DependencyProperty.Register("ReferenceView", typeof(MapView), typeof(MiniMap), new PropertyMetadata(ReferenceViewChanged));

        public static readonly DependencyProperty MiniMapViewProperty =
            DependencyProperty.Register("MiniMapView", typeof(MapView), typeof(MiniMap), new PropertyMetadata(MiniMapViewChanged));

        // the actual map control used by the minimap
        public IMap MiniMapMapControl
        {
            get { return (IMap)GetValue(MiniMapMapControlProperty); }
            set { SetValue(MiniMapMapControlProperty, value); }
        }

        public static readonly DependencyProperty MiniMapMapControlProperty =
            DependencyProperty.Register("MiniMapMapControl", typeof(IMap), typeof(MiniMap), null);

        
        public BaseTileSourceProvider TileSourceProvider
        {
            get { return (BaseTileSourceProvider)GetValue(TileSourceProviderProperty); }
            set { SetValue(TileSourceProviderProperty, value); }
        }

        public static readonly DependencyProperty TileSourceProviderProperty =
            DependencyProperty.Register("TileSourceProvider", typeof(BaseTileSourceProvider), typeof(MiniMap), new PropertyMetadata(MapModePropertyChanged));

        private static void MapModePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            MiniMap self = sender as MiniMap;

            if (args.NewValue != null && self.MiniMapView != null)
            {
                self.MiniMapView.TileSourceProvider = args.NewValue as BaseTileSourceProvider;
            }
        }

        bool hasFocus;
        ContentControl mapWrapper;

        public double ZoomModifier
        {
            get { return (double)GetValue(ZoomModifierProperty); }
            set { SetValue(ZoomModifierProperty, value); }
        }

        public MapView ReferenceView
        {
            get { return (MapView)GetValue(ReferenceViewProperty); }
            set { SetValue(ReferenceViewProperty, value); }
        }

        public MapView MiniMapView
        {
            get { return (MapView)GetValue(MiniMapViewProperty); }
            set { SetValue(MiniMapViewProperty, value); }
        }

        // This is going to be required for correct behaviour of automation controls e.g. Map Robot
        // false by default.
        // We're using a "hasFocus" property to disable a feedback loop
        public bool DisableSynchronisation { get; set; }

        // Can the minimap be zoomed?
        private bool enableZoom = false;
        public bool EnableZoom
        {
            get
            {
                return enableZoom;
            }
            set
            {
                enableZoom = value;

                if (MiniMapMapControl != null)
                {
                    MiniMapMapControl.DisableZoom = !enableZoom;
                }
            }
        }

        private static void ReferenceViewChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            var self = sender as MiniMap;
            if (self == null) return;

            var oldReferenceView = args.OldValue as MapView;
            if (oldReferenceView != null)
            {
                oldReferenceView.PropertyChanged -= self.ReferenceView_PropertyChanged;
            }

             var newReferenceView = args.NewValue as MapView;
             if (newReferenceView != null)
            {
                newReferenceView.PropertyChanged += self.ReferenceView_PropertyChanged;

                self.MiniMapView = newReferenceView.Clone();
            }
        }

        private static void MiniMapViewChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            var self = sender as MiniMap;
            if (self == null) return;

            var oldMiniMapView = args.OldValue as MapView;
            if (oldMiniMapView != null)
            {
                oldMiniMapView.PropertyChanged -= self.ActualView_PropertyChanged;
            }

            var newMiniMapView = args.NewValue as MapView;
            if (newMiniMapView != null)
            {
                newMiniMapView.PropertyChanged += self.ActualView_PropertyChanged;
            }
        }

        #endregion

        public MiniMap()
        {
            DefaultStyleKey = typeof(MiniMap);
        }

        protected override void OnMouseEnter(MouseEventArgs e)
        {
            hasFocus = true;
        }

        protected override void OnMouseLeave(MouseEventArgs e)
        {
            hasFocus = false;
        }

        void ReferenceView_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case MapView.VIEWPORT_WIDTH :
                case MapView.ZOOM_LEVEL :
                case MapView.CENTER :
                    SyncMiniMapView();
                    break;
            }
        }

        private void SyncMiniMapView()
        {
            if (!hasFocus || !DisableSynchronisation)
            {
                MiniMapView.SetLocation(ReferenceView.Center, ReferenceView.ViewportWidth * ZoomModifier);
            }
        }

        void ActualView_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case MapView.PROVIDER_TILESOURCES:
                case MapView.TILE_SOURCE_PROVIDER :
                    MiniMapMapControl.TileSourceProvider = MiniMapView.TileSourceProvider;
                    break;
                case MapView.VIEWPORT_WIDTH:
                case MapView.ZOOM_LEVEL:
                case MapView.CENTER:
                    if (hasFocus && !DisableSynchronisation)
                    {
                        ReferenceView.SetLocation(MiniMapView.Center, MiniMapView.ViewportWidth / ZoomModifier);
                    }
                    break;
            }
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            mapWrapper = (ContentControl) GetTemplateChild(PART_map);

            if (Map != null)
            {
                SetMapContent();
            }
        }

        protected override void MapChanged(IMap newMap, IMap oldMap)
        {
            if (mapWrapper != null)
            {
                SetMapContent();
            }
        }

        void SetMapContent()
        {
            MiniMapMapControl = Map.CreateInstance();
            MiniMapMapControl.DisableZoom = !EnableZoom;

            // We can't bind the Reference directly to the Minimap because we'd like to apply a transformation
            // on the view every time a view property changes - namely the ZoomModifier

            var referenceBinding = new Binding(MiniMapMapControl.ViewDependencyPropertyPath) { Source = Map };
            SetBinding(ReferenceViewProperty, referenceBinding);

            var intermedBinding = new Binding("MiniMapView") { Source=this, Mode=BindingMode.OneWay };
            MiniMapMapControl.AsFrameworkElement.SetBinding(MiniMapMapControl.ViewDependencyProperty, intermedBinding);

            mapWrapper.Content = MiniMapMapControl;

            if (TileSourceProvider != null && MiniMapView != null)
            {
                MiniMapView.TileSourceProvider = TileSourceProvider;
            }
  
            //sync initially
            SyncMiniMapView();
        }
      
    }
}
